package br.usjt.component.login.beans;

import static br.com.usjt.component.commons.login.util.SHAConverter.encrypts;
import static br.com.usjt.component.commons.login.util.ValidationUtil.isNull;
import static br.com.usjt.component.commons.login.util.ValidationUtil.not;
import static br.usjt.component.login.assembler.LoginAssembler.converterParaListaLoginDTOUsando;
import static br.usjt.component.login.assembler.LoginAssembler.converterParaLoginDTOUsando;
import static br.usjt.component.login.assembler.LoginAssembler.converterParaLoginUsando;
import static br.usjt.component.login.assembler.LoginAssembler.toEntityAttached;

import java.util.List;

import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

import br.com.usjt.component.commons.login.exceptions.BadCredentialsException;
import br.com.usjt.component.commons.login.exceptions.NotResultException;
import br.usjt.component.login.dto.LoginDTO;
import br.usjt.component.login.facade.LoginBeanRemote;
import br.usjt.component.login.facade.dao.LoginDao;
import br.usjt.component.login.facade.dao.PerfilDao;
import br.usjt.component.login.facade.dao.UsuarioDao;
import br.usjt.component.login.model.Login;
import br.usjt.component.login.model.Perfil;
import br.usjt.component.login.model.Usuario;

@Stateless
@Remote(LoginBeanRemote.class)
@LocalBean
public class LoginBean extends AbstractBean implements LoginBeanRemote {

    private static final long serialVersionUID = 575362268965564125L;

    @Inject
    private LoginDao loginDao;
    @Inject
    private UsuarioDao usuarioDao;
    @Inject
    private PerfilDao perfilDao;

    @EJB
    private LoginSingletonBean sessionLogin;

    public LoginBean() {
    }

    @Override
    public void salvar(LoginDTO dto) {
        validarDTO(dto);
        Login login = converterParaLoginUsando(dto);
        Perfil perfil = login.getPerfil();
        Usuario usuario = login.getUsuario();

        login.setUsuario(null);
        login.setPerfil(null);
        criptografarSenha(login);
        loginDao.save(login);

        associaPerfil(login, perfil);
        associaUsuario(login, usuario);
    }

    @Override
    public void atualizar(LoginDTO dto) {
        validarDTO(dto);
        Login loginConvertido = converterParaLoginUsando(dto);

        criptografarSenha(loginConvertido);

        Perfil perfil = loginConvertido.getPerfil();

        Login loginAttached = consultarPorId(loginConvertido.getId());
        toEntityAttached(loginConvertido, loginAttached);
        associaPerfil(loginAttached, perfil);

        loginDao.update(loginAttached);
    }

    private void criptografarSenha(Login loginConvertido) {
        loginConvertido.setSenha(modificarParaCriptografia(loginConvertido.getSenha()));
    }

    private String modificarParaCriptografia(String senha) {
        return encrypts(senha);
    }

    @Override
    public LoginDTO efetuarLogin(String username, String senha) {

        String senhaCriptografada = modificarParaCriptografia(senha);
        validarInformacao(username, senhaCriptografada);
        try {
            Login login = loginDao.efetuarLogin(username, senhaCriptografada);
            validarLoginSession(login);
            sessionLogin.addLogins(login);
            return converterParaLoginDTOUsando(login);
        } catch (NotResultException e) {
            throw new BadCredentialsException("The login name or password is incorrect. Please try again.", e);
        }

    }

    @Override
    public LoginDTO efetuarLogin(LoginDTO dto) {
        validarDTO(dto);
        return efetuarLogin(dto.getLogin(), dto.getSenha());
    }

    @Override
    public List<LoginDTO> consultarTodosLogins() {
        List<Login> listaLogins = loginDao.findByAll();
        return converterParaListaLoginDTOUsando(listaLogins);
    }

    @Override
    public List<LoginDTO> consultarLoginsAtivos() {
        List<Login> listaLogins = loginDao.findByAllActives();
        return converterParaListaLoginDTOUsando(listaLogins);
    }

    @Override
    public List<LoginDTO> consultarLoginsInativos() {
        List<Login> listaLogins = loginDao.findByAllInactives();
        List<LoginDTO> listaDtosLogins = converterParaListaLoginDTOUsando(listaLogins);

        return listaDtosLogins;
    }

    @Override
    public void renovarSenha(LoginDTO dto) {
        validarDTO(dto);
        validarInformacao(dto.getId());
        Login loginAux = consultarPorId(dto.getId());
        validaAlteracaoSenha(dto, loginAux);
        atualizar(dto);
    }

    private Login consultarPorId(Integer id) {
        return loginDao.findById(id);
    }

    private void validaAlteracaoSenha(LoginDTO dto, Login loginAux) {
        if (loginAux.getSenha().equals(dto.getSenha()) || loginAux.getSenha().equals(modificarParaCriptografia(dto.getSenha()))) {
            throw new BadCredentialsException("Senha Atual invalida!");
        }
    }

    @Override
    public void logout(LoginDTO dto) {
        validarDTO(dto);

        Login login = converterParaLoginUsando(dto);

        Login login2 = consultarPorId(login.getId());

        sessionLogin.removeLogins(login2);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private void associaUsuario(Login login, Usuario usuario) {
        if (not(isNull(usuario)) && not(isNull(usuario.getId()))) {
            Usuario usuarioAux = usuarioDao.findById(usuario.getId());
            login.setUsuario(usuarioAux);
            usuarioAux.setLogin(login);
            loginDao.update(login);
        } else {
            login.setUsuario(usuario);
            loginDao.update(login);
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private void associaPerfil(Login login, Perfil perfil) {
        if (not(isNull(perfil)) && not(isNull(perfil.getId()))) {
            Perfil loginAux = perfilDao.findById(perfil.getId());
            login.setPerfil(loginAux);
            loginDao.update(login);
        } else {
            login.setPerfil(perfil);
            loginDao.update(login);
        }
    }

    private void validarLoginSession(Login login) {
        if (sessionLogin.constainLogin(login)) {
            throw new BadCredentialsException("Usuario já em sessão");
        }
    }
}
